package net.tools.web;

import java.io.*;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

abstract class TunnelSerializable	//different to java.io.Serializable, byte stream is much smaller
{

	private long seq;

	private byte cmd;

	private int len;

	private byte[] data = new byte[65536];

	private TeeInputStream tin = new TeeInputStream();

	private ByteArrayOutputStream baout = new ByteArrayOutputStream();

	private DataOutputStream dout = new DataOutputStream(baout);

	private DataInputStream din = new DataInputStream(tin);

	private byte[] sha1sum = new byte[20];

	private byte[] checksum = new byte[20];

	private MessageDigest sha1md;

	{
		try
		{
			sha1md = MessageDigest.getInstance("sha1");
		}
		catch (NoSuchAlgorithmException e)
		{
			e.printStackTrace();
		}
	}

	long getSeq()
	{
		return seq;
	}

	void setSeq(long sequence)
	{
		this.seq = sequence;
	}

	byte getCmd()
	{
		return cmd;
	}

	void setCmd(byte command)
	{
		this.cmd = command;
	}

	int getLen()
	{
		return len;
	}

	void setLen(int dataLength)
	{
		this.len = dataLength;
	}

	byte[] getData()
	{
		return data;
	}

	byte[] getSha1sum()
	{
		return sha1sum;
	}

	byte[] getChecksum()
	{
		return checksum;
	}

	abstract void writeBodyTo(DataOutputStream out) throws IOException;

	abstract void readBodyFrom(DataInputStream in) throws IOException;

	void writeTo(OutputStream out) throws IOException, DigestException
	{
		writeBodyTo(dout);
		dout.flush();
		byte[] buf = baout.toByteArray();
		out.write(buf);
		sha1md.update(buf);
		if(sha1md.digest(buf, 0, buf.length) != buf.length)
			throw new DigestException("length not equal");
		baout.reset();
		dout.write(sha1sum);
		dout.flush();
		buf = baout.toByteArray();
		out.write(buf);
		baout.reset();
	}

	void readFrom(InputStream in) throws IOException, DigestException
	{
		tin.init(in, baout);
		readBodyFrom(din);
		baout.flush();
		byte[] buf = baout.toByteArray();
		sha1md.update(buf);
		if(sha1md.digest(checksum, 0, checksum.length) != checksum.length)
			throw new DigestException("length not equal");
		din.readFully(sha1sum);
		if(!MessageDigest.isEqual(sha1sum, checksum))
			throw new DigestException("Command checksum data error.  Maybe attack?");
		baout.reset();
		tin.init(null, null);
	}

}
